其他
写爬虫,怎么可以不会正则呢?
以下文章来源于大龄码农的Python之路 ,作者丹枫无迹
导读:正则在各语言中的使用是有差异的,本文以 Python 3 为基础。本文主要讲述的是正则的语法,对于 re 模块不做过多描述,只会对一些特殊地方做提示。
1、正则基础
1.1、基础语法
re.search('\d+', s).group()
re.search('\w+', s).group()
123abc你好
1.2、修饰符
A
使 \w
只匹配 ASCII 字符,\W
匹配非 ASCII 字符。re.search('\w+', s, re.A).group()
re.search('\W+', s, re.A).group()
你好
\d
和 \D
,数字不都是 ASCII 字符吗?这是什么意思?别忘了,还有 全角和半角!re.search('\d+', s, re.U).group()
re.findall('^[\s\w]*?$', s)
re.findall('^[\s\w]*?$', s, re.M)
['aaa\r', 'bbb\r', 'ccc'] # 多行模式
re.findall('^.*', s)
re.findall('^.*', s, re.S)
['aaa\r\nbbb\r\nccc']
\d+ # 匹配数字
# 和字母
[a-zA-Z]+
""", re.X)
rc.search('123abc').group()
注意,用了 X
修饰符后,正则中的所有空格会被忽略,包括正则里面的原本有用的空格。如果正则中有需要使用空格,只能用\s
代替。
(?aiLmsux)
表示了以上所有的修饰符,具体用的时候需要哪个就在 ? 后面加上对应的字母,示例如下,(?a)
和 re.A
效果是一样的:re.search('(?a)\w+', s).group()
re.search('\w+', s, re.A).group()
123abc
1.3、贪婪与懒惰
re.search('a.*b', s).group() # 这就是贪婪
re.search('a.*?b', s).group() # 这就是懒惰
aab
所谓贪婪,就是尽可能 多 的匹配;
所谓懒惰,就是尽可能 少 的匹配。
*
、+
、{n,}
这些表达式属于贪婪;*?
、+?
、{n,}?
这些表达式就是懒惰(在贪婪的基础上加上?
)。
2、正则进阶
2.1、捕获分组
(?<name>exp)
或 (?'name'exp)
,但在 Python 里,这样写会报错:This named group syntax is not supported in this regex dialect。Python 中正确的写法是:(?P<name>exp)
m = re.search('姓名[::](\w+).*?电话[::](\d{11})', s)
if m:
name = m.group(1)
phone = m.group(2)
print(f'name:{name}, phone:{phone}')
(?P<name>exp)
有时还是会用到的, (?P=name)
则很少情况下会用到。我想了一个 (?P=name)
的使用示例,给大家看下效果:<name>张三</name>
<age>30</age>
<phone>138123456789</phone>
'''
pattern = r'<(?P<name>.*?)>(.*?)</(?P=name)>'
It = re.findall(pattern, s)
2.2、零宽断言
(?<=exp)
和前项否定界定 (?<!exp)
在 Python 中可能会报错:look-behind requires fixed-width pattern,原因是 python 中 前项界定的表达式必须是定长的,看如下示例:(?<=aaa|bbb) # 正确
(?<=aaa|bb) # 错误
(?<=\d+) # 错误
(?<=\d{3}) # 正确
2.3、条件匹配
s2 = 'abc1'
pattern = '(_)?[a-zA-Z]+(?(1)[a-zA-Z]|\d)'
re.search(pattern, s1).group()
re.search(pattern, s2).group()
abc1
2.4、findall
re.findall
是个比较特别的方法(之所以说它特别,是跟我常用的 C# 做比较,在没看注释之前我想当然的掉坑里去了)。我们看这个方法的官方注释:If one or more capturing groups are present in the pattern, return
a list of groups; this will be a list of tuples if the pattern
has more than one group.
Empty matches are included in the result.
如果没有分组,则返回整条正则匹配结果的列表;
如果有 1 个分组,则返回分组匹配到的结果的列表;
如果有多个分组,则返回分组匹配到的结果的元组的列表。
re.findall('[a-z]+\d+', s) # 不包含分组
re.findall('[a-z]+(\d+)', s) # 包含一个分组
re.findall('([a-z]+(\d+))', s) # 包含多个分组
re.findall('(?:[a-z]+(\d+))', s) # ?: 不捕获分组匹配结果
['123', '456']
[('aaa123', '123'), ('bbb456', '456')]
['123', '456']
零宽断言中讲到 Python 中前项界定必须是定长的,这很不方便,但是配合 findall 有分组时只取分组结果的特性,就可以模拟出非定长前项界定的效果了。
结语
◆
精彩推荐
◆
「2019 AI开发者大会」 除了邀请国内外一线公司重磅嘉宾外,还邀请到了亚马逊首席科学家@李沐,他将于9月5日亲授「深度学习实训营」,通过动手实操,帮助开发者全面了解深度学习的基础知识和开发技巧。
社群福利
扫码添加小助手,回复:大会,加入2019 AI开发者大会福利群,每周更新技术福利,还有不定期的抽奖活动~推荐阅读